{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "07ff8c7b",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "#  13\\.  K 近邻回归算法实现与应用  # \n",
    "\n",
    "##  13.1.  介绍  # \n",
    "\n",
    "K 近邻算法实验中，我们学习了将其用于分类问题的解决思路。实际上，K 近邻亦可用于回归分析预测。本次挑战中，你将完成对 K 近邻算法改造，将其应用于回归分析。 \n",
    "\n",
    "##  13.2.  知识点  # \n",
    "\n",
    "  * K 近邻回归介绍 \n",
    "\n",
    "  * K 近邻回归实现 \n",
    "\n",
    "##  13.3.  内容回顾  # \n",
    "\n",
    "回顾我们在 K 近邻实验中学习过的内容。当使用 K 近邻算法完成分类任务时，需要的步骤有： \n",
    "\n",
    "  * 数据准备：通过数据清洗，数据处理，将每条数据整理成向量。 \n",
    "\n",
    "  * 计算距离：计算测试数据与训练数据之间的距离。 \n",
    "\n",
    "  * 寻找邻居：找到与测试数据距离最近的 K 个训练数据样本。 \n",
    "\n",
    "  * 决策分类：根据决策规则，从 K 个邻居得到测试数据的类别。 \n",
    "\n",
    "[ ![https://cdn.aibydoing.com/aibydoing/images/document-uid214893labid7506timestamp1546417333161.gif](https://cdn.aibydoing.com/aibydoing/images/document-uid214893labid7506timestamp1546417333161.gif) ](https://cdn.aibydoing.com/aibydoing/images/document-uid214893labid7506timestamp1546417333161.gif)\n",
    "\n",
    "其中，「决策分类」是决定未知样本类别的关键步骤。那么，当我们将 K 近邻算法用于回归预测时，实际上只需要将这一步修改为适合于回归问题的流程即可。 \n",
    "\n",
    "  * 分类问题：根据 K 个邻居的类别，多数表决得到未知样本的类别。 \n",
    "\n",
    "  * 回归问题：根据 K 个邻居的目标值，计算平均值得到未知样本的预测值。 \n",
    "\n",
    "K 近邻回归算法图示如下： \n",
    "\n",
    "[ ![https://cdn.aibydoing.com/aibydoing/images/document-uid214893labid7506timestamp1546420986145.jpg](https://cdn.aibydoing.com/aibydoing/images/document-uid214893labid7506timestamp1546420986145.jpg) ](https://cdn.aibydoing.com/aibydoing/images/document-uid214893labid7506timestamp1546420986145.jpg)\n",
    "\n",
    "接下来，你需要根据上面的图示和说明，实现 K 近邻回归算法，并用示例数据进行验证。 \n",
    "\n",
    "Exercise 13.1 \n",
    "\n",
    "挑战：根据上述图示和说明，实现 K 近邻回归算法。 \n",
    "\n",
    "规定：距离计算使用欧式距离公式，部分代码可以参考实验内容。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f8def1eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "def knn_regression(train_data, train_labels, test_data, k):\n",
    "    \"\"\"\n",
    "    参数:\n",
    "    train_data -- 训练数据特征 numpy.ndarray.2d\n",
    "    train_labels -- 训练数据目标 numpy.ndarray.1d\n",
    "    test_data -- 测试数据特征 numpy.ndarray.2d\n",
    "    k -- k 值\n",
    "\n",
    "    返回:\n",
    "    test_labels -- 测试数据目标 numpy.ndarray.1d\n",
    "    \"\"\"\n",
    "\n",
    "    ### 代码开始 ### (≈ 10 行代码)\n",
    "    test_labels = None\n",
    "    ### 代码结束 ###\n",
    "\n",
    "    return test_labels"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82619d58",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "参考答案  Exercise 13.1 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a79e0552",
   "metadata": {},
   "outputs": [],
   "source": [
    "def knn_regression(train_data, train_labels, test_data, k):\n",
    "    \"\"\"\n",
    "    参数:\n",
    "    train_data -- 训练数据特征 numpy.ndarray.2d\n",
    "    train_labels -- 训练数据目标 numpy.ndarray.1d\n",
    "    test_data -- 测试数据特征 numpy.ndarray.2d\n",
    "    k -- k 值\n",
    "\n",
    "    返回:\n",
    "    test_labels -- 训练数据目标 numpy.ndarray.1d\n",
    "    \"\"\"\n",
    "\n",
    "    ### 代码开始 ### (≈ 10 行代码)\n",
    "    test_labels = np.array([])  # 创建一个空的数组用于存放预测结果\n",
    "    for X_test in test_data:\n",
    "        distances = np.array([])\n",
    "        for each_X in train_data:  # 使用欧式距离计算数据相似度\n",
    "            d = np.sqrt(np.sum(np.square(X_test - each_X)))\n",
    "            distances = np.append(distances, d)\n",
    "        sorted_distance_index = distances.argsort()  # 获取按距离大小排序后的索引\n",
    "        k_labels = train_labels[sorted_distance_index[:k]]\n",
    "        y_test = np.mean(k_labels)\n",
    "        test_labels = np.append(test_labels, y_test)\n",
    "    ### 代码结束 ###\n",
    "\n",
    "    return test_labels"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "345ec299",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "下面，我们提供一组测试数据。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8acfa047",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "# 训练样本特征\n",
    "train_data = np.array(\n",
    "    [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10]]\n",
    ")\n",
    "# 训练样本目标值\n",
    "train_labels = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d8816ff",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "运行测试 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f8288c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试样本特征\n",
    "test_data = np.array([[1.2, 1.3], [3.7, 3.5], [5.5, 6.2], [7.1, 7.9]])\n",
    "# 测试样本目标值\n",
    "knn_regression(train_data, train_labels, test_data, k=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a104092c",
   "metadata": {
    "lines_to_next_cell": 0
   },
   "source": [
    "期望输出 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8091c708",
   "metadata": {},
   "outputs": [],
   "source": [
    "array([2., 4., 6., 7.])"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
